home *** CD-ROM | disk | FTP | other *** search
/ ftp.qualcomm.com / 2014.06.ftp.qualcomm.com.tar / ftp.qualcomm.com / eudora / developers / emsapi / carbon_emsapi.sit.hqx / Macintosh API Support / rfc822.c < prev    next >
Text File  |  2001-03-08  |  8KB  |  302 lines

  1. /* =========================================================================
  2.  
  3.     Functions for very basic RFC-822 header manipulation
  4.              
  5.     Filename:            rfc822.c
  6.     Last Edited:        March 7, 1997
  7.     Authors:            Laurence Lundblade, Myra Callen, Bob Fronabarger
  8.     Copyright:            1995, 1996 QUALCOMM Inc.
  9.     Technical support:    <emsapi-info@qualcomm.com>
  10.  
  11.     Some of this code is from the c-client and is 
  12.         Copyright University of Washington
  13. */
  14.  
  15. #include <string.h>
  16. #include "CopyCat.h"
  17. #include "RFC822.h"
  18.  
  19.  
  20. /* All these special characters will confuse CodeWarrior so you may not get
  21.  * the colorized comments and key words you are use to.
  22.  */
  23. extern const char *hSpecials = " ()<>@,;:\\\042";        /* parse-host specials (\042 = ") */
  24. extern const char *wSpecials = " ()<>@,;:\\\042[]";        /* parse-word specials */
  25. extern const char *ptSpecials = " ()<>@,;:\\\042[]/?=";    /* parse-token specials */
  26.  
  27. const char *kRFC822_MustBeQuoted = "()<>@,;:\\\042/[]?= ";
  28. const char *kRFC822_MustBeEscaped = "\\\042";
  29.  
  30. char *RFC822_UnquoteStrCpy(char *dst, const char *src, unsigned int len);
  31.  
  32.  
  33. /* =========================================================================
  34.  *  Find next RFC822 token in given string, copying it into a newly created
  35.  *  string. Advance pointer past token and any following whitespace.
  36.  *   
  37.  *  NOTE: The user of this function is responsible for freeing returned string.
  38.  *
  39.  *  Args:     cpp [IN/OUT] Handle (pointer-to-pointer) of RFC822 string to extract from
  40.  *
  41.  *  Returns: String containing next token, nil if error.
  42.  *        Moves cpp to first non-whitespace character AFTER extracted token
  43.  */
  44. char *RFC822_ExtractToken(char **cpp)
  45. {
  46.     char    *start, *buf, *cp = *cpp;
  47.     short    len;
  48.  
  49.     start = cp = RFC822_SkipWS(cp); // Skip white space
  50.  
  51.     cp = RFC822_SkipWord(cp);        // Skip word
  52.     if (cp > start) {
  53.         len = cp - start;
  54.         buf = NewPtr(len + 1);
  55.         if (buf) {
  56.             RFC822_UnquoteStrCpy(buf, start, len);
  57.             *cpp = RFC822_SkipWS(cp);
  58.             return buf;
  59.         }
  60.     }
  61.     return nil; // No token to extract
  62. }
  63.  
  64.  
  65. /* =========================================================================
  66.  *    Skips RFC822 whitespace
  67.  *
  68.  *    Args:     Text string
  69.  *
  70.  *    Returns: Moves cpp to first non-whitespace character
  71.  */
  72. char *RFC822_SkipWS(char *cp)
  73. {
  74.     long    nested;
  75.     
  76.     do {
  77.         while (*cp == ' ')        // Skip spaces
  78.             cp++;
  79.  
  80.         if (*cp == '(') {        // A comment?
  81.             nested = 1;
  82.             while ((*++cp) && nested) {                 // Find end of comment
  83.                 switch (*cp) {
  84.                     case '(':
  85.                         nested++;
  86.                         break;
  87.                     case ')':
  88.                         nested--;
  89.                         break;
  90.                     case '\\':    // Escape character
  91.                         if ((*(cp + 1)) != '\0')        // Check for end-of-string
  92.                             cp++;
  93.                         break;
  94.                     case '"':    // Quote inside comment
  95.                         while (*cp && (*++cp != '"'))    // Go 'til end of quote
  96.                             if ((*cp == '\\') && ((*(cp + 1)) != '\0'))
  97.                                 cp++; // Escaped character inside quote -- inside comment (eeek!)
  98.                         break;
  99.                 }
  100.             }
  101.         }
  102.     } while (*cp == ' ');
  103.     return cp;
  104. }
  105.  
  106.  
  107. /* =========================================================================
  108.  *  Advances to next character in string directly after the current token.
  109.  *  The first character must be the beginning of a valid token or end of
  110.  *  string (ie. all whitespace must be skipped BEFORE calling this function).
  111.  *
  112.  *  Args:     cp [IN] RFC822 string
  113.  *
  114.  *  Returns: Pointer to next valid whitespace character.
  115.  */
  116. char *RFC822_SkipWord(char *cp)
  117. {
  118.     Boolean        bInQuotes = false;
  119.  
  120.     if (*cp == '"') {    // First character must be a quote to be valid double-quoted string
  121.         bInQuotes = true;
  122.         cp++;
  123.     }
  124.     while (*cp) {
  125.         if (strchr(kRFC822_MustBeQuoted, *cp)) {
  126.             if (!bInQuotes)        // Found a character that should be double-quoted, but is not
  127.                 return (cp);
  128.             switch (*cp) {        // Now we know we are inside a double-quoted string
  129.                 case '"':
  130.                     return (cp + 1); // End double-quote
  131.                 case '\\':
  132.                     cp++;        // Escape character, skip next
  133.                     break;
  134.             }
  135.         }
  136.         cp++;
  137.     }
  138.     return cp;
  139. }
  140.  
  141.  
  142. /* =========================================================================
  143.  *  Calculates the length of the given text string if it were converted
  144.  *  to an RFC822 string.
  145.  *
  146.  *  Args:     cp [IN] Text string
  147.  *
  148.  *  Returns: Equivalent RFC822 length of given text.
  149.  */
  150. unsigned short RFC822_QuotedStrLen(StringPtr theStr)
  151. {
  152.     unsigned short    len = 0;
  153.     char            s[256], *cp;    // i know these strings are short
  154.     Boolean            quoted = false;
  155.  
  156.     BlockMoveData(theStr, s, theStr[0] + 1);
  157.     PtoCstr((StringPtr) s);
  158.     cp = s;
  159.     while (*cp) {
  160.         len++;
  161.         if (!quoted && (strchr(kRFC822_MustBeQuoted, *cp) != nil))
  162.             quoted = true;
  163.         if (strchr(kRFC822_MustBeEscaped, *cp) != nil) {
  164.             len++;
  165.             quoted = true;
  166.         }
  167.         cp++;
  168.     }
  169.     if (quoted)
  170.         len += 2; // Quotes
  171.     return len;
  172. }
  173.  
  174.  
  175. /* =========================================================================
  176.  *  Copies and converts the source text string to a destination
  177.  *  RFC822 string. The source must be NULL terminated, and the
  178.  *  destination will be NULL terminated.
  179.  *
  180.  *  Args:     dst [OUT] RFC822 string
  181.  *             src [IN]  Text string
  182.  *
  183.  *  Returns: Pointer to destination NULL termination.
  184.  */
  185. char *RFC822_QuoteStrCpy(char *dst, StringPtr theStr)
  186. {
  187.     char        *src;
  188.     Boolean        quoted;
  189.  
  190.     PtoCstr(theStr);
  191.     src = (char*) theStr;
  192.     quoted = (strpbrk(src, kRFC822_MustBeQuoted) != nil);
  193.     if (quoted)
  194.         *dst++ = '"';
  195.     while (*src) {
  196.         if (strchr(kRFC822_MustBeEscaped, *src) != nil)
  197.             *dst++ = '\\';
  198.         *dst++ = *src++;
  199.     }
  200.     if (quoted)
  201.         *dst++ = '"';
  202.     *dst = '\0';
  203.     CtoPstr((char*) theStr);
  204.     return dst;
  205. }
  206.  
  207.  
  208. /* =========================================================================
  209.  *  Copies and converts the source RFC822 string to a destination
  210.  *  text string. The source must be null terminated, and the
  211.  *  destination will be null terminated.
  212.  *
  213.  *  Args:     dst [OUT] Text string
  214.  *             src [IN]  RFC822 string
  215.  *              len [IN]  Maximum charcters to copy; Zero implies whole string
  216.  *
  217.  *  Returns: Pointer to destination nil termination.
  218.  */
  219. char *RFC822_UnquoteStrCpy(char *dst, const char *src, unsigned int len)
  220. {
  221.     char        *end;
  222.     Boolean        escaped = false;
  223.  
  224.     end = strchr(src, '\0');
  225.     if ((len > 0) && ((src + len) < end))
  226.         end = (char*) src + len;
  227.     while (src < end) {
  228.         if (escaped)
  229.             escaped = false;
  230.         else {
  231.             switch (*src) {
  232.                 case '\\':
  233.                     escaped = true;
  234.                 case '"':
  235.                     src++;
  236.                     continue;
  237.             }
  238.         }
  239.         *dst++ = *src++;
  240.     }
  241.     *dst = '\0';
  242.     return dst;
  243. }
  244.  
  245.  
  246. /* =========================================================================
  247.  *  Finds and extracts a header line from a full multi-lined header. All
  248.  *  unfolding (removing newlines) is done before header line is returned.
  249.  *
  250.  *  NOTE: The user of this function is responsible for freeing the
  251.  *        returned string.
  252.  *
  253.  *  Args:     pFullHeader [IN] Pointer to a full RFC822 header, including newlines
  254.  *             pLinePrefix [IN] Prefix of header line to extract
  255.  *
  256.  *  Returns: Extracted header line string; dynamically allocated.
  257.  */
  258. char *RFC822_ExtractHeader(const char *pFullHeader, const char *pLinePrefix)
  259. {
  260.     const char                *kNewline = "\r\n";
  261.     const unsigned short    kNewlineLen = 2;
  262.     const unsigned short    nPreLen = strlen(pLinePrefix);
  263.     char                    *pStart, *pEnd, *pBuf, *pPos, *pRetBuf;
  264.  
  265.     if (nPreLen < 1)
  266.         return (nil);
  267.  
  268.     pStart = (char*) pFullHeader;
  269.  
  270.     // Find first 'line' which matches prefix
  271.     while ((pStart) && (strnicmp(pStart, pLinePrefix, nPreLen) != 0)) {
  272.         pStart = strstr(pStart, kNewline);
  273.         if (pStart)
  274.             pStart += kNewlineLen;
  275.     }
  276.     if (!pStart)
  277.         return nil;        // Not found
  278.  
  279.     // Find the end of this header line
  280.     for (pEnd = strstr(pStart, kNewline); (pEnd != nil); pEnd = strstr(pEnd, kNewline)) {
  281.         pPos = pEnd + kNewlineLen;
  282.         if ((*pPos == ' ') || (*pPos == '\t')) // Does header line continue on next line?
  283.             pEnd = pPos;
  284.         else
  285.             break;
  286.     }
  287.  
  288.     // If we ran off the end of the string, then the end is the last char in the string
  289.     if (pEnd == nil)
  290.         pEnd = strchr(pStart, '\0');
  291.     
  292.     pBuf = pRetBuf = NewPtr(pEnd - pStart + 1);    // Max length of output string
  293.     for (pPos = pStart; pPos < pEnd; pPos++) {
  294.         if (strncmp(pPos, kNewline, kNewlineLen) == 0)    // We're at a newline
  295.             pPos += kNewlineLen;    // Skip over newline
  296.         *pBuf++ = *pPos;
  297.     }
  298.     *pBuf = '\0';
  299.  
  300.     return pRetBuf;
  301. }
  302.